#include "ParticleSystem.h"
#include "Game.h"
#include "TileSystem.h"
#include <string>
#include "../TinyXML/tinyxml.h"

// particle system instance variable
ParticleSystem* ParticleSystem::pInstance = nullptr;

// SINGLETON ACCESSOR!!
ParticleSystem* ParticleSystem::GetInstance()
{
	if (pInstance == nullptr)
		pInstance = new ParticleSystem;

	return pInstance;
}

// SINGLETON
void ParticleSystem::DeleteInstance()
{
	delete pInstance;
	pInstance = nullptr;
}

// load ALL emitterFiles from a directory (used for emitterMap)
int ParticleSystem::LoadEmitters()
{
	int numLoaded = 0;

	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;

	wchar_t* newPath = L"resource/ParticleFiles/*";

	hFind = FindFirstFile(newPath, &FindFileData);
	do {
		// ignore directories
		if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
		{
			wstring name = FindFileData.cFileName;
			if (name[name.find_last_of(L".") + 1] == 'p') //Is it a pxml?
			{
				char newName[128];
				wcstombs_s(nullptr, newName, name.c_str(), name.length());
				newName[name.length()] = '\0';

				string fullName = "resource/ParticleFiles/";
				fullName.append(newName);

				string truncName = newName;
				truncName.erase(truncName.find_first_of("."));

				//Load the emitter
				Emitter *newEmitter = LoadEmitterXML(fullName);
				emitterMap.emplace(truncName, newEmitter);

				newEmitter = nullptr;

				numLoaded++;
			}
		}
	} while (FindNextFile(hFind, &FindFileData) != 0);

	return numLoaded;
}

// load a single Emitter from an XML file
Emitter* ParticleSystem::LoadEmitterXML(string file, MovingObject* what)
{
	Emitter* loadingEmitter = new Emitter();

	//Create the XML Document
	TiXmlDocument doc(file.c_str());

	//Attempt to load
	if (doc.LoadFile() == false)
		return nullptr;

	TiXmlHandle docHandle(&doc);
	TiXmlHandle rootHandle(0);

	//Access the root element
	TiXmlElement* rootPointer;
	rootPointer = doc.RootElement();

	if (rootPointer == nullptr)
		return nullptr;

	loadingEmitter->SetIsLooping(reinterpret_cast<bool>(rootPointer->Attribute("isLooping")));
	string imageName = rootPointer->Attribute("imageName");
	loadingEmitter->SetImageName(imageName);

	string imageFullPath = "resource/Graphics/";
	imageFullPath += imageName;

	loadingEmitter->SetParticleImage(SGD::GraphicsManager::GetInstance()->LoadTexture(imageFullPath.c_str()));

	// access the first child (Emitter - empty)
	TiXmlElement* element = rootPointer->FirstChildElement("Emitter");
	// access Emitters first child (new ptr so element can switch later)
	TiXmlElement* dataElement = element->FirstChildElement("emitterType");

	loadingEmitter->SetEmitType(atoi(dataElement->Attribute("type")));

	dataElement = dataElement->NextSiblingElement("numParticles");
	loadingEmitter->SetMaxNumParticles(atoi(dataElement->Attribute("particles")));

	dataElement = dataElement->NextSiblingElement("lineRotation");
	loadingEmitter->SetLineRotation(static_cast<float>(atof(dataElement->Attribute("rotation"))));

	dataElement = dataElement->NextSiblingElement("spawnRate");
	loadingEmitter->SetSpawnRate(static_cast<float>(atof(dataElement->Attribute("rate"))));

	dataElement = dataElement->NextSiblingElement("position");

	if (what == nullptr)
	{
		// if particle not attached to something, import xml position
		loadingEmitter->SetPosition({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

		loadingEmitter->SetIsAttached(true);
	}
	else
	{
		SGD::Point tempPos;
		tempPos.x = what->GetPosition().x - Game::GetInstance()->GetCameraPos().x;
		tempPos.y = what->GetPosition().y - Game::GetInstance()->GetCameraPos().y;

		tempPos.x += what->GetSize().width / 2;
		tempPos.y -= what->GetSize().height / 2;

		// attached to something, attach to it.
		loadingEmitter->SetPosition(tempPos);
		loadingEmitter->SetIsAttached(true);
		loadingEmitter->SetObjectOwner(what);
	}

	dataElement = dataElement->NextSiblingElement("circleVector");
	loadingEmitter->SetCircleVector({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	dataElement = dataElement->NextSiblingElement("lineVector");
	loadingEmitter->SetLineVector({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	dataElement = dataElement->NextSiblingElement("emitterRectSize");
	loadingEmitter->SetEmitterRectSize({ static_cast<float>(atof(dataElement->Attribute("Width"))), static_cast<float>(atof(dataElement->Attribute("Height"))) });

	// switch to Particle info
	element = element->NextSiblingElement("Particle");

	dataElement = element->FirstChildElement("maxParticleAge");
	loadingEmitter->SetMaxParticleAge(static_cast<float>(atof(dataElement->Attribute("age"))));

	dataElement = dataElement->NextSiblingElement("startRotation");
	loadingEmitter->SetStartRotation(static_cast<float>(atof(dataElement->Attribute("rotation"))));

	dataElement = dataElement->NextSiblingElement("endRotation");
	loadingEmitter->SetEndRotation(static_cast<float>(atof(dataElement->Attribute("rotation"))));

	dataElement = dataElement->NextSiblingElement("startColor");
	unsigned char A = atoi(dataElement->Attribute("A"));
	unsigned char R = atoi(dataElement->Attribute("R"));
	unsigned char G = atoi(dataElement->Attribute("G"));
	unsigned char B = atoi(dataElement->Attribute("B"));

	loadingEmitter->SetStartColor(SGD::Color(A, R, G, B));

	dataElement = dataElement->NextSiblingElement("endColor");
	A = atoi(dataElement->Attribute("A"));
	R = atoi(dataElement->Attribute("R"));
	G = atoi(dataElement->Attribute("G"));
	B = atoi(dataElement->Attribute("B"));

	loadingEmitter->SetEndColor(SGD::Color(A, R, G, B));

	dataElement = dataElement->NextSiblingElement("startVelocity");
	loadingEmitter->SetStartVelocity({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	dataElement = dataElement->NextSiblingElement("endVelocity");
	loadingEmitter->SetEndVelocity({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	dataElement = dataElement->NextSiblingElement("startScale");
	loadingEmitter->SetStartScale({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	dataElement = dataElement->NextSiblingElement("endScale");
	loadingEmitter->SetEndScale({ static_cast<float>(atof(dataElement->Attribute("X"))), static_cast<float>(atof(dataElement->Attribute("Y"))) });

	// create particleList
	if (loadingEmitter->GetParticleList().empty())
	{
		for (int i = 0; i < loadingEmitter->GetMaxNumParticles(); i++)
			loadingEmitter->GetParticleList().push_back(new Particles(loadingEmitter));
	}

	// for each Emitter
	return loadingEmitter;
}

// exit the system
void ParticleSystem::Exit()
{
	for (map<string, Emitter*>::iterator i = emitterMap.begin(); i != emitterMap.end(); ++i)
	{
		delete i->second;
		i->second = 0; // I don't think this is strictly necessary...
	}
	emitterMap.clear();
}